/*
 * aki-draw.c - Tool for generating bitmap patterns for aki-ledsuite
 *
 * Copyright (C) 2009 Free Software Initiative of Japan
 *
 * Written by Wiktor Langowski <jolkaczad@gmail.com>
 *
 * Distributed under GPL General Public License version 3 (or later)
 */

/*
 * This simple program helps generating a list of hexadecimal integers
 * needed to display text or graphics using the Akizuki 32x16
 * LED matrix. At the moment the file format it generates is the same
 * used in Niibe Yutaka's program available at
 * https://members.fsij.org/trac/soc2009/attachment/wiki/Aki-Software/aki-usb-20090519.tgz
 * though it's simple enough to change it to whatever format desirable
 * by editing the print2file() function.
 *
 * compilation:
 * $ gcc -Wall -lncurses -o aki-draw aki-draw.c
 * run:
 * $ ./aki-draw
 *
 * nothing fancy here. all you need is ncurses - I used 5.6 but I guess
 * older and never versions will do to, as only the library's basic
 * functionality is used. The code may not be optimal but I did write it on
 * a Thinkpad T20 (PIII 700) so any machine should suffice
 */

/*
 * TODO: when reviewing the code next time, the character generating
 * option must be edited to include text width as the first element (tab[0])
 * of every drawn letter. Now that I think of it, perhaps the size should
 * be included with every bitmap generated, also the drawings and stuff
 *
 * TODO: fix that annoying thing in paintbrush mode, so that it doesn't
 * toggle the LEDs when you don't want them to
 *
 * TODO: it's not always desirable to overwrite a pattern that's being edited.
 * saving a pattern with a chosen name is an important feature to implement
 */

#include <ncurses.h>	/* ncurses include stdio */
#include <time.h>
#include <string.h>

#define HEIGHT	16+2	/* the twos are added for the borders to have */
#define WIDTH	32*2+2

#define PATT_HEIGHT	16
#define PATT_WIDTH	32

void ncurses_init();
void ncurses_close();
void window_draw();
void manage_keys();
void key_left();
void key_right();
void key_up();
void key_down();
void redraw_canvas();
void key_space();
void print2ihex();
void print2array();
void print_keyconf();
void clear_matrix();
void add_to_ihex(int a, FILE *file);
void fopen_pattern(char *argv[]);

WINDOW *canvas_win;

/* the array will be iterated from 1 upwards so to avoid
 * having to substract 1 every time while copying the cursor
 * or character position */

int canvas_buff[WIDTH+1][HEIGHT+1];

int cur_x;
int cur_y;

int ac;
char **av;

int address;

int main(int argc, char *argv[])
{
	ac= argc;
	av= argv;

	ncurses_init();
	print_keyconf();
	window_draw();

	if(argc!=1){
		fopen_pattern(argv);
	}

	manage_keys();

	ncurses_close();

	return 0;
}
void ncurses_init()
{
	initscr();
	cbreak();	/* allows the characters to be available just as soon
				   as the user types them. ctrl-z ctrl-c work as in 
				   any other terminal program */
	noecho();	/* turns of echoing of characters, obviously */
	keypad(stdscr, TRUE);	/* this enables reading the F1, F2.. keys
							   as well as the arrows */

	refresh();
	
	return;
}
void window_draw()
{
	canvas_win= newwin(HEIGHT, WIDTH, 0, 0);
	box(canvas_win, 0, 0);
	wborder(canvas_win, 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H');

	cur_x= 1;
	cur_y= 1;

	refresh();

	wmove(canvas_win, cur_y, cur_x);
	wrefresh(canvas_win);
	
	return;
}
void ncurses_close()
{
	delwin(canvas_win);
	endwin();

	return;
}
void manage_keys()
{
	int ch;
	while((ch= getch()) != KEY_F(1)){
		switch(ch){
			case 'a':
				key_left();
				break;

			case 'd':
				key_right();
				break;

			case 'w':
				key_up();
				break;

			case 's':
				key_down();
				break;

			case ' ':
				key_space();
				break;

			case 'p':
				print2ihex();
				break;

			case 'o':
				print2array();
				break;
			
			case 'A':
				key_space();
				key_left();
				break;

			case 'D':
				key_space();
				key_right();
				break;

			case 'W':
				key_space();
				key_up();
				break;

			case 'S':
				key_space();
				key_down();
				break;

			case 'c':
				clear_matrix();
				break;

		}
		wmove(canvas_win, cur_y, cur_x);

		wrefresh(canvas_win);
	}
}
void key_left()
{
	if(cur_x-2>=1){
		cur_x--;
		cur_x--;
	}
	else
		;
}
void key_right()
{
	if(cur_x+2<=WIDTH-3){
		cur_x++;
		cur_x++;
	}
	else
		;
}
void key_down()
{
	if(cur_y+1<=HEIGHT-2){
		cur_y++;
	}
	else
		;
}
void key_up()
{
	if(cur_y-1>=1){
		cur_y--;
	}
	else
		;
}
void key_space()
{
	int *a= &canvas_buff[cur_x][cur_y];
	int *b= &canvas_buff[cur_x+1][cur_y];

	if(*a){
		*a= 0;
		*b= 0;
	}
	else{
		*a= 1;
		*b= 1;
	}
	
	redraw_canvas();

	return;
}
void redraw_canvas()
{
	int i;
	int j;

	int x= cur_x;
	int y= cur_y;
	
	for(i=1; i<WIDTH-1; i++){
		for(j=1; j<HEIGHT-1; j++){
			if(canvas_buff[i][j])
				mvwaddch(canvas_win, j, i, 'K');
			
			else
				mvwaddch(canvas_win, j, i, ' ');
		}
	}
	wrefresh(canvas_win);
	refresh();

	cur_x= x;
	cur_y= y;

	return;
}
void print2ihex()
{
	FILE *file;
	time_t time_now;
	struct tm *broken_now;

	char date_formated[27];

	int i, j;
	int vert_i;

	int x= cur_x;
	int y= cur_y;

	int a= 0;
	
	time_now= time(NULL);
	broken_now= localtime(&time_now);
	strftime(date_formated, 20, "%d%m%y-%a-%H%M%S", broken_now);

	strcat(date_formated, ".ee.hex\0");
	file= fopen(date_formated, "w");

	for(i=1; i<WIDTH-2; i+=2){
		vert_i= 15;
		for(j= HEIGHT-2; j>=1; j--){
			if(canvas_buff[i][j])
				a+= 1 << (vert_i%8);

			if(!(vert_i%8)){
				add_to_ihex(a, file);
				a= 0;
			}
			vert_i--;
		}
	}
	
	/* EOF */
	/*FIXME this still may lack flushing what remained */
	fprintf(file, ":00000001FF\r\n");
	address= 0;
	fclose(file);
	
	
	mvprintw(HEIGHT+1, 0, "saved as %s", date_formated);
	cur_x= x;
	cur_y= y;

	refresh();
	return;
}
void add_to_ihex(int a, FILE *file)
{
	static int checksum;
	static int i= 0;
	static char the_array[27];
	
	int j= 0;

	/* array character roll call
	 * 1	':'
	 * 2	number of bytes in line (always set to 8)
	 * 4	address in EEPROM (8 added every line)
	 * 2	line type: 00 for data, 01 for EOF
	 * 16	data padded with zeros at the end if less than 8 bytes
	 * 2	checksum (all bytes in a line must sum to 0x00
	 * ----
	 * + 27
	 */
/*	int x= cur_x;
	int y= cur_y;

	mvprintw(HEIGHT+1+i, 0, "%d", i);
	cur_x= x;
	cur_y= y;*/

	the_array[0]= ':';
	the_array[1]= '0';
	the_array[2]= '8';
	
	if(!i){
		checksum= 8;

		for(j=3; j<26; j++)
			the_array[j]= '0';

		sprintf(&the_array[3], "%.4X", address);
		checksum+= address;
		address+= 8;

		/* the line type comes now but it doesn't have to be explicitly
		 * written since the array is zeroed.
		 * edit: yes it does because the previous sprintf adds a null
		 * at the end of it's part */
		the_array[7]= '0';
		the_array[8]= '0';
		
		i= 9;
	}

	sprintf(&the_array[i], "%.2X", a);
	checksum+= a;
	i+=2;

	if(i==25){
		/* end of line, almost */
		checksum%= 0x100;
		checksum= 0x100 - checksum;

		sprintf(&the_array[i], "%.2X\r\n", checksum);

		fprintf(file, "%s", the_array);

		i= 0;
	}

	return;
}
void print2array()
{
	FILE *file;
	time_t time_now;
	struct tm *broken_now;

	char date_formated[20];

	int i, j;
	int vert_i;

	int x= cur_x;
	int y= cur_y;

	int a=0;

	if(ac==1){
		time_now= time(NULL);
		broken_now= localtime(&time_now);
		strftime(date_formated, 20, "%d%m%y-%a-%H%M%S", broken_now);

		file= fopen(date_formated, "w");
	}
	else{
		file= fopen(av[1], "w");
	}

	for(i=1; i<WIDTH-2; i+=2){

		vert_i= 15;
		for(j=HEIGHT-2; j>=1; j--){
			if(canvas_buff[i][j])
				a+= 1 << (vert_i%8);

			if(!(vert_i%8)){
				fprintf(file, "0x%X,\n", a);
				a=0;
			}
			vert_i--;
		}
	}

	fclose(file);

	if(ac==1)
		mvprintw(HEIGHT+1, 0, "saved as %s", date_formated);
	else
		mvprintw(HEIGHT+1, 0, "saved as %s", av[1]);

	cur_x= x;
	cur_y= y;

	refresh();


	return;
}
void clear_matrix()
{
	int i, j;

	for(i=1; i<WIDTH-1; i++){
		for(j=1; j<HEIGHT-1; j++)
			canvas_buff[i][j]= 0;
	}

	redraw_canvas();
}
void print_keyconf()
{
	mvprintw(HEIGHT+3, 0, "F1 \t Exit");
	mvprintw(HEIGHT+4, 0, "Space \t Toggle LED lit/dim");
	mvprintw(HEIGHT+5, 0, "p \t Print the setting to an Intel HEX file");
	mvprintw(HEIGHT+6, 0, "o \t Print the setting in 'array format'");
	mvprintw(HEIGHT+7, 0, "\t Use WSAD to navigate");
	mvprintw(HEIGHT+8, 0, "\t Hold shift for paintbrush mode");
	mvprintw(HEIGHT+9, 0, "c \t Clear the matrix");

	return;
}
void fopen_pattern(char *argv[])
{
	/* this function must be fixed to enable drawing screens of multiple
	 * heigth */
	FILE *file;
	int a;
	int read_buff[WIDTH*HEIGHT/8];

	int i;
	int j;

	int col_it;

	int iflow;

	unsigned int element;

	file= fopen(argv[1], "r");

	i= 0;
	while(fscanf(file, "0x%X,\n", &a)!=EOF)
		read_buff[i++]= a;


	col_it= -2;
	for(i= 0; i<PATT_WIDTH*PATT_HEIGHT/8; i++){
		element= read_buff[i];

		/* switching between the lower and upper half of a column */
		if(!(i%2)){
			iflow= 8;
			col_it+=2;
		}
		
		else{
			iflow= 0;
		}

		for(j= 0; j<8; j++){
			/* checking if the bit for the ($iflow+j)th column is set */
			if((element >> j)%2){
				canvas_buff[col_it+1][j+iflow+1]= 1;
				canvas_buff[col_it+1+1][j+iflow+1]= 1;
			}
		}
	}
	redraw_canvas();
}
